import random
import string
from typing import Optional
import redis
import mmh3
from fastapi import Depends

from app.core.config import settings
from app.db.session import get_db
from app.db.models import URL
from app.schemas.shortener import URLCreate
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

redis_client = redis.Redis(
    host=settings.REDIS_HOST,
    port=settings.REDIS_PORT,
    db=settings.REDIS_DB,
    decode_responses=True  # 添加这个参数，确保Redis返回的是字符串而不是字节
)

def generate_short_code(url_in: URLCreate, rand: str = '') -> str:
    """生成基于 MurmurHash32 和 Base62 的 6 位短码"""
    url = url_in.original_url
    # 计算 MurmurHash32 并转换为无符号 32 位整数
    hash_value = mmh3.hash(str(url) + rand)
    unsigned_hash = hash_value & 0xFFFFFFFF
    # 转换为 Base62 并补齐到 6 位
    encoded = base62_encode(unsigned_hash).ljust(6, '0')[:6]

    # 检查 Redis+MySQL 中是否存在
    if check_code(encoded):
        # 若有相同的短码, 则再次生成
        encoded = generate_short_code(url_in, encoded)

    return encoded  # 确保长度固定为6位

def base62_encode(num: int) -> str:
    """将整数编码为 Base62 字符串"""
    chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    if num == 0:
        return '0'
    encoded = []
    while num > 0:
        num, rem = divmod(num, 62)
        encoded.append(chars[rem])
    # 反转余数顺序得到正确编码
    return ''.join(reversed(encoded))


async def check_code(code: str, db: AsyncSession = Depends(get_db)) -> bool:
    """检查短码是否在数据库中存在"""
    # 检查 Redis 中是否存在
    if redis_client.exists(f"url:{code}"):
        return True
    url = await db.execute(
        select(URL).where(URL.short_code == code)
    )
    url = url.scalar_one_or_none()
    return url is not None  # 如果存在，返回 True


def cache_url(short_code: str, original_url: str, expire_seconds: int = None) -> None:
    """缓存短链接映射"""
    redis_client.set(
        f"url:{short_code}",
        original_url,  # 不需要encode，因为redis_client会自动处理
        ex=expire_seconds
    )

def get_cached_url(short_code: str) -> str:
    """获取缓存的原始URL"""
    return redis_client.get(f"url:{short_code}") 